package ru.papergames.battleserver.util.pathfinding;

import ru.common.util.Point3D;
import ru.papergames.battleserver.model.battle.npc.NpcBattleMap;
import ru.papergames.battleserver.model.battle.npc.NpcCell;

import java.util.ArrayList;
import java.util.List;

/**
 * @author: atroshanin
 * Date: 31.07.2010
 * Time: 23:01:26
 */
public class SimplePathFinder implements PathFinder {

    private NpcBattleMap map;

    public SimplePathFinder(NpcBattleMap map) {
        this.map = map;
    }

    public List<Point3D> findPath(int sx, int sy, int tx, int ty) {
        int dx = tx - sx;
        int vectorX = (dx > 0) ? 1 : ((dx < 0) ? -1 : 0);
        int dy = ty - sy;
        int vectorY = (dy > 0) ? 1 : ((dy < 0) ? -1 : 0);

        dx = dx < 0 ? -dx : dx;
        dy = dy < 0 ? -dy : dy;

        List<Point3D> result = new ArrayList<Point3D>(dx > dy ? dx : dy);

        int curX = sx;
        int curY = sy;
        result.add(new Point3D(curX, curY));

        int d1 = dx - dy;
        int d2 = dy - dx;
        int d3 = (dx > dy) ? dy : dx;

        for (int i=0; i < d1; i++) {
            curX += vectorX;
            if (!checkCell(curX, curY)) {
                if (i != d1 - 1) {
                    if (checkCell(curX + vectorX, curY)) {
                        if (checkCell(curX, curY + vectorY)) {
                            curY += vectorY;
                            result.add(new Point3D(curX, curY));
                            curY -= vectorY;
                            curX += vectorX;
                            result.add(new Point3D(curX, curY));
                            i++;
                            continue;
                        } else if (checkCell(curX, curY - vectorY)) {
                            curY -= vectorY;
                            result.add(new Point3D(curX, curY));
                            curY += vectorY;
                            curX += vectorX;
                            result.add(new Point3D(curX, curY));
                            i++;
                            continue;
                        }
                    }
                } else {
                    if (d3 != 0) {
                        if (checkCell(curX + vectorX, curY + vectorY)) {
                            if (checkCell(curX, curY + vectorY)) {
                                curY += vectorY;
                                result.add(new Point3D(curX, curY));

                                curX += vectorX;
                                result.add(new Point3D(curX, curY));
                                d3--;
                                continue;
                            } else if (checkCell(curX, curY - vectorY) && checkCell(curX + vectorX, curY)) {
                                curY -= vectorY;
                                result.add(new Point3D(curX, curY));

                                curX += vectorX;
                                curY += vectorY;
                                result.add(new Point3D(curX, curY));

                                curY += vectorY;
                                result.add(new Point3D(curX, curY));
                                d3--;
                                continue;
                            }

                        }
                    }
                }
                // here we can say that we can not find path, so we will just return dirty path
                // map = null;
                return null;
            }
            result.add(new Point3D(curX, curY));
        }


        for (int i=0; i < d2; i++) {
            curY += vectorY;
            if (!checkCell(curX, curY)) {
                if (i != d2 - 1) {
                    if (checkCell(curX, curY + vectorY)) {
                        if (checkCell(curX + vectorX, curY)) {
                            curX += vectorX;
                            result.add(new Point3D(curX, curY));
                            curX -= vectorX;
                            curY += vectorY;
                            result.add(new Point3D(curX, curY));
                            i++;
                            continue;
                        } else if (checkCell(curX - vectorX, curY)) {
                            curX -= vectorX;
                            result.add(new Point3D(curX, curY));
                            curY += vectorY;
                            curX += vectorX;
                            result.add(new Point3D(curX, curY));
                            i++;
                            continue;
                        }
                    }
                } else {
                    if (d3 != 0) {
                        if (checkCell(curX + vectorX, curY + vectorY)) {
                            if (checkCell(curX + vectorX, curY)) {
                                curX += vectorX;
                                result.add(new Point3D(curX, curY));

                                curY += vectorY;
                                result.add(new Point3D(curX, curY));
                                d3--;
                                continue;
                            } else if (checkCell(curX - vectorX, curY) && checkCell(curX, curY + vectorY)) {
                                curX -= vectorX;
                                result.add(new Point3D(curX, curY));

                                curX += vectorX;
                                curY += vectorY;
                                result.add(new Point3D(curX, curY));

                                curX += vectorX;
                                result.add(new Point3D(curX, curY));
                                d3--;
                                continue;
                            }

                        }
                    }
                }
                // here we can say that we can not find path, so we will just return dirty path
                // map = null;
                return null;
            }
            result.add(new Point3D(curX, curY));
        }

        for (int i=0; i<d3; i++) {
            curX += vectorX;
            curY += vectorY;
            if (!checkCell(curX, curY)) {
                if (i != d3 - 1) {
                    if (checkCell(curX + vectorX, curY + vectorY)) {
                        if (checkCell(curX - vectorX, curY) && checkCell(curX, curY + vectorY)) {
                            curX -= vectorX;
                            result.add(new Point3D(curX, curY));

                            curX += vectorX;
                            curY += vectorY;
                            result.add(new Point3D(curX, curY));

                            curX += vectorX;
                            result.add(new Point3D(curX, curY));
                            continue;
                        } else if (checkCell(curX, curY - vectorY) && checkCell(curX + vectorX, curY)) {
                            curY -= vectorY;
                            result.add(new Point3D(curX, curY));

                            curX += vectorX;
                            curY += vectorY;
                            result.add(new Point3D(curX, curY));

                            curY += vectorY;
                            result.add(new Point3D(curX, curY));
                            continue;
                        }

                    }
                }
                // here we can say that we can not find path, so we will just return dirty path
                // map = null;
                return null;
            }

            result.add(new Point3D(curX, curY));
        }

        return result;
    }

    private boolean checkCell(int x, int y) {
        if (map == null) {
            return true;
        }
        NpcCell cell = map.getCell(x, y);
        return cell != null && cell.isCellFree();

    }
}
